package generalEvolutionaryAlgorithm;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class EvolutionaryAlorithm {

	public static EAResult doEvolutionaryAlgorithm(EABlackBoxFunction function, 
													EAInstanceGenerator generator, 
													EASelection evolutionarySelection,
													EACrossover crossover,
													EAMutation mutator, 
													EASelection environmentalSelection, 
													int startingPopulationSize, 
													int numberOfSteps){
		return doEvolutionaryAlgorithm(function, generator, evolutionarySelection, crossover, mutator, environmentalSelection, startingPopulationSize, numberOfSteps, "");
	}
	public static EAResult doEvolutionaryAlgorithm(EABlackBoxFunction function, 
													EAInstanceGenerator generator, 
													EASelection evolutionarySelection,
													EACrossover crossover,
													EAMutation mutator, 
													EASelection environmentalSelection, 
													int startingPopulationSize, 
													int numberOfSteps,
													String identifier){
		// generate population
		List<EAInstance> population = new ArrayList<EAInstance>();
		for(int i = 0; i < startingPopulationSize; i++){
			population.add(generator.generateInstance(function));
		}
		EAResult result = null;
		for(EAInstance instance: population){
			instance.calculateResult();
			result = EAResult.best(result, instance.getResult());
		}
		int printFirstSteps = 0; // how many of the first swarms will surely be printed
		double printEach = 0.1; //proportion of printed swarms
		int lastPrintedStep = 0;
		for(int step = 0; true; ++step){
			// do some logging:
			if(step <= printFirstSteps || step == numberOfSteps || step >= lastPrintedStep + printEach * numberOfSteps){
				log(identifier + "logIteration" + step, population);
				common.Logging.logData(result.optimalInstance.toString() + result.optimalValue.toString(), identifier + "bestResultLogIteration" + step);
				System.out.println("After " + step + "steps: "
						+ " global optimal found value: " + result.optimalValue.toString()
						+ " corresponding position: " + result.optimalInstance.toString()
						+ " used black box function calls: " + function.getNumberOfQueries());
				lastPrintedStep = step;
			}
			if(step == numberOfSteps)break;
			// evolutionary selection:
			List<EAInstance> selectedParents = evolutionarySelection.selection(population);
			// generate children:
			List<EAInstance> children = crossover.generateChildren(selectedParents, result);
			List<EAInstance> mutatedChildren = new LinkedList<EAInstance>();
			for(EAInstance instance: children){
				EAInstance mutatedInstance = mutator.mutate(instance);
				mutatedInstance.calculateResult();
				mutatedChildren.add(mutatedInstance);
				result = EAResult.best(result, instance.getResult());
			}
			// join children to population:
			population.addAll(mutatedChildren);
			// environmental selection:
			population = environmentalSelection.selection(population);
		}
		return result;
	}
	

	
	private static void log(String filePath, List<EAInstance> population){
		String data = "";
			for(EAInstance p:population){
				data = data + p.toString();
			}
		common.Logging.logData(data, filePath);
	}
}
